﻿using Microsoft.ExtendedReflection.ComponentModel;
using Microsoft.ExtendedReflection.Monitoring;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.TestAdapter;
using System;
using System.Diagnostics;
using System.Threading;

namespace MolesVS2012.Host
{
    [Serializable]
    internal abstract class MolesAdapterBase : ComponentBase, ITestAdapter, IBaseAdapter, IRunContextManager, IService
    {
        private IRunContext runContext;
        private IExecutionRunConfiguration _executionRunConfiguration;
        private TestRun _testRun;
        private TestRunConfiguration _testRunConfiguration;
        private IResultSink _resultSink;
        private Guid _runGuid;
        private ApartmentState? _apartmentState;

        public IRunContext RunContext
        {
            get
            {
                return this.runContext;
            }
        }

        public IExecutionRunConfiguration ExecutionRunConfiguration
        {
            get
            {
                if (this._executionRunConfiguration == null)
                {
                    using (_ProtectingThreadContext.Acquire())
                        this._executionRunConfiguration = this.runContext.RunConfig;
                }
                return this._executionRunConfiguration;
            }
        }

        public TestRun TestRun
        {
            get
            {
                if (this._testRun == null)
                {
                    using (_ProtectingThreadContext.Acquire())
                        this._testRun = this.ExecutionRunConfiguration.TestRun;
                }
                return this._testRun;
            }
        }

        public TestRunConfiguration TestRunConfiguration
        {
            get
            {
                if (this._testRunConfiguration == null)
                {
                    using (_ProtectingThreadContext.Acquire())
                        this._testRunConfiguration = this.TestRun.RunConfiguration;
                }
                return this._testRunConfiguration;
            }
        }

        public ApartmentState ApartmentState
        {
            get
            {
                if (!this._apartmentState.HasValue)
                {
                    using (_ProtectingThreadContext.Acquire())
                    {
                        this._apartmentState = new ApartmentState?(this.TestRunConfiguration.ApartmentState);
                        ApartmentState? nullable = this._apartmentState;
                        if ((nullable.GetValueOrDefault() != ApartmentState.Unknown ? 0 : (nullable.HasValue ? 1 : 0)) != 0)
                            this._apartmentState = new ApartmentState?(ApartmentState.STA);
                    }
                }
                return this._apartmentState.Value;
            }
        }

        public Guid RunGuid
        {
            get
            {
                if (this._runGuid == Guid.Empty)
                {
                    using (_ProtectingThreadContext.Acquire())
                        this._runGuid = this.TestRun.Id;
                }
                return this._runGuid;
            }
        }

        public IResultSink ResultSink
        {
            get
            {
                if (this._resultSink == null)
                {
                    using (_ProtectingThreadContext.Acquire())
                        this._resultSink = this.runContext.ResultSink;
                }
                return this._resultSink;
            }
        }

        public virtual void Initialize(IRunContext runContext)
        {
            Trace.TraceInformation("Pex: Initialize");
            this.runContext = runContext;
        }

        public abstract void Run(ITestElement testElement, ITestContext testContext);

        public abstract void AbortTestRun();

        public abstract void PauseTestRun();

        public abstract void ResumeTestRun();

        public abstract void StopTestRun();

        public abstract void Cleanup();

        public abstract void ReceiveMessage(object obj);

        public abstract void PreTestRunFinished(IRunContext runContext);
    }
}
